/**
 * Xinfei.com Inc.
 * Copyright (c) 2004-2023 All Rights Reserved.
 */
package com.xinfei.techplayprod.biz.aop;

import com.dianping.cat.proxy.Tracer;
import com.dianping.cat.proxy.spi.Transaction;
import com.xinfei.techplayprod.biz.log.BaseDigestLog;
import com.xinfei.techplayprod.biz.log.DigestLogAnnotated;
import com.xinfei.techplayprod.biz.log.DigestLogHolder;
import com.xinfei.techplayprod.biz.model.enums.TechplayErrDtlEnum;
import com.xinfei.techplayprod.biz.model.exception.TechplayException;
import com.xinfei.techplayprod.biz.util.ExceptionUtil;
import com.xinfei.techplayprod.biz.util.LoggerUtil;
import com.xinfei.xfframework.common.BaseResponse;
import org.aopalliance.intercept.MethodInvocation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Method;

/**
 * 摘要日志拦截器
 *
 * @author Jinyan.Huang
 * @version $ DigestLogInterceptor, v 0.1 2023/8/28 16:25 Jinyan.Huang Exp $
 */
@SuppressWarnings("unused")
public class DigestLogInterceptor extends BaseInterceptor {

    @Override
    public Object businessInvoke(MethodInvocation invocation) throws Throwable {

        // 1、检查，如果配了拦截器但是没配注解的，一律报错
        if (!invocation.getMethod().isAnnotationPresent(DigestLogAnnotated.class)) {
            throw new TechplayException(TechplayErrDtlEnum.UNKNOWN_EXCEPTION, "摘要日志注解配置错误,method=" + invocation.getMethod().getName());
        }

        // 构造 cat transaction
        Method method = invocation.getMethod();
        Transaction transaction = Tracer.newTransaction(method.getDeclaringClass().getSimpleName(), method.getName());

        // 2、清空日志上下文存放器
        DigestLogHolder.clear();

        Object result = null;
        long elapse = -1;

        try {

            // 3、计算调用时长
            long startTime = System.currentTimeMillis();

            // 调用执行
            result = invocation.proceed();

            elapse = System.currentTimeMillis() - startTime;

            return result;

        } catch (Exception e) {

            ExceptionUtil.error(e);
            throw new TechplayException(TechplayErrDtlEnum.UNKNOWN_EXCEPTION, e);

        } finally {

            if (result != null && DigestLogHolder.get() != null) {
                try {

                    // 5、打印摘要日志
                    printDigestLog(method, elapse, result, transaction);
                    // Cat Transaction 输出
                    transaction.complete();

                } catch (Exception e) {
                    ExceptionUtil.error(e, "摘要日志输出现异常：");
                }
            }

            DigestLogHolder.clear();
        }
    }

    // ~~~ 内部方法

    /**
     * 打印摘要日志。
     *
     * @param method      被代理的方法
     * @param elapse      调用时长
     * @param result      调用结果
     * @param transaction CatTransaction, 可为null
     */
    private void printDigestLog(Method method, long elapse, Object result, Transaction transaction) {

        BaseDigestLog digestLog = DigestLogHolder.get();

        // 补全日志
        completeDigestLog(digestLog, method, elapse, result, transaction);

        // 从注解中获取日志名称
        String loggerName = method.getAnnotation(DigestLogAnnotated.class).value();
        Logger digestLogger = LoggerFactory.getLogger(loggerName);

        // 日志输出到文件
        LoggerUtil.info(digestLogger, digestLog.toDigestLog());
    }

    /**
     * 补全摘要日志。
     *
     * @param baseDigestLog 摘要日志对象
     * @param method        被代理的方法
     * @param elapse        调用时长
     * @param result        调用结果
     * @param transaction   CatTransaction
     */
    private void completeDigestLog(BaseDigestLog baseDigestLog, Method method, long elapse,
                                   Object result, Transaction transaction) {

        String invocationSignature = method.getDeclaringClass().getSimpleName() + "."
                + method.getName();

        baseDigestLog.setElapse(elapse);
        baseDigestLog.setInvocationMethod(invocationSignature);

        BaseResponse baseResult = (BaseResponse) result;
        baseDigestLog.setSuccess(baseResult.isSuc());

        baseDigestLog.setErrorContext(baseResult.getErrorContext());

        if (transaction != null) {
            if (baseResult.isSuc()) {
                transaction.setStatus(Transaction.SUCCESS);
            } else if (baseResult.getErrorContext() != null) {
                transaction.setStatus(baseResult.getErrorContext().getErrCode());
            }
        }
    }
}
